#include "U2ConfigFile.h"

#include "U2Exception.h"
#include "U2ResourceGroupManager.h"



U2EG_NAMESPACE_USING


//-----------------------------------------------------------------------
U2ConfigFile::U2ConfigFile()
{
}
//-----------------------------------------------------------------------
U2ConfigFile::~U2ConfigFile()
{
    SettingsBySection::iterator seci, secend;
    secend = mSettings.end();
    for (seci = mSettings.begin(); seci != secend; ++seci)
    {
        U2_DELETE_T(seci->second, SettingsMultiMap, MEMCATEGORY_GENERAL);
    }
}
//-----------------------------------------------------------------------
void U2ConfigFile::clear(void)
{
    for (SettingsBySection::iterator seci = mSettings.begin(); 
        seci != mSettings.end(); ++seci)
    {
        U2_DELETE_T(seci->second, SettingsMultiMap, MEMCATEGORY_GENERAL);
    }
    mSettings.clear();
}
//-----------------------------------------------------------------------
void U2ConfigFile::load(const U2String& filename, const U2String& separators, bool trimWhitespace)
{
    loadDirect(filename, separators, trimWhitespace);
}
//-----------------------------------------------------------------------
void U2ConfigFile::load(const U2String& filename, const U2String& resourceGroup, 
                      const U2String& separators, bool trimWhitespace)
{
    loadFromResourceSystem(filename, resourceGroup, separators, trimWhitespace);
}
//-----------------------------------------------------------------------
void U2ConfigFile::loadDirect(const U2String& filename, const U2String& separators, 
                            bool trimWhitespace)
{
    /* Open the configuration file */
    std::ifstream fp;
    // Always open in binary mode
    fp.open(filename.c_str(), std::ios::in | std::ios::binary);
    if(!fp)
        U2_EXCEPT(U2Exception::ERR_FILE_NOT_FOUND, "'" + filename + "' file not found!", "U2ConfigFile::load" );

    // Wrap as a stream
    U2DataStreamPtr stream(U2_NEW U2DataStreamFileStream(filename, &fp, false));
    load(stream, separators, trimWhitespace);

}
//-----------------------------------------------------------------------
void U2ConfigFile::loadFromResourceSystem(const U2String& filename, 
                                        const U2String& resourceGroup, const U2String& separators, bool trimWhitespace)
{
    U2DataStreamPtr stream = 
        U2ResourceGroupManager::getSingleton().openResource(filename, resourceGroup);
    load(stream, separators, trimWhitespace);
}
//-----------------------------------------------------------------------
void U2ConfigFile::load(const U2DataStreamPtr& stream, const U2String& separators, 
                      bool trimWhitespace)
{
    /* Clear current settings map */
    clear();
    U2String currentSection = U2StringUtil::BLANK;
    SettingsMultiMap* currentSettings = U2_NEW_T(SettingsMultiMap, MEMCATEGORY_GENERAL)();
    mSettings[currentSection] = currentSettings;

    /* Process the file line for line */
    U2String line, optName, optVal;
    while (!stream->eof())
    {
        line = stream->getLine();
        /* Ignore comments & blanks */
        if (line.length() > 0 && line.at(0) != '#' && line.at(0) != '@')
        {
            if (line.at(0) == '[' && line.at(line.length()-1) == ']')
            {
                // Section
                currentSection = line.substr(1, line.length() - 2);
                SettingsBySection::const_iterator seci = mSettings.find(currentSection);
                if (seci == mSettings.end())
                {
                    currentSettings = U2_NEW_T(SettingsMultiMap, MEMCATEGORY_GENERAL)();
                    mSettings[currentSection] = currentSettings;
                }
                else
                {
                    currentSettings = seci->second;
                } 
            }
            else
            {
                /* Find the first seperator character and split the string there */
                std::string::size_type separator_pos = line.find_first_of(separators, 0);
                if (separator_pos != std::string::npos)
                {
                    optName = line.substr(0, separator_pos);
                    /* Find the first non-seperator character following the name */
                    std::string::size_type nonseparator_pos = line.find_first_not_of(separators, separator_pos);
                    /* ... and extract the value */
                    /* Make sure we don't crash on an empty setting (it might be a valid value) */
                    optVal = (nonseparator_pos == std::string::npos) ? "" : line.substr(nonseparator_pos);
                    if (trimWhitespace)
                    {
                        U2StringUtil::trim(optVal);
                        U2StringUtil::trim(optName);
                    }
                    currentSettings->insert(std::multimap<U2String, U2String>::value_type(optName, optVal));
                }
            }
        }
    }
}
//-----------------------------------------------------------------------
U2String U2ConfigFile::getSetting(const U2String& key, const U2String& section, const U2String& defaultValue) const
{

    SettingsBySection::const_iterator seci = mSettings.find(section);
    if (seci == mSettings.end())
    {
        return defaultValue;
    }
    else
    {
        SettingsMultiMap::const_iterator i = seci->second->find(key);
        if (i == seci->second->end())
        {
            return defaultValue;
        }
        else
        {
            return i->second;
        }
    }
}
//-----------------------------------------------------------------------
U2StringVector U2ConfigFile::getMultiSetting(const U2String& key, const U2String& section) const
{
    U2StringVector ret;

    SettingsBySection::const_iterator seci = mSettings.find(section);
    if (seci != mSettings.end())
    {
        SettingsMultiMap::const_iterator i;

        i = seci->second->find(key);
        // Iterate over matches
        while (i != seci->second->end() && i->first == key)
        {
            ret.push_back(i->second);
            ++i;
        }
    }
    return ret;
}
//-----------------------------------------------------------------------
U2ConfigFile::SettingsIterator U2ConfigFile::getSettingsIterator(const U2String& section)
{
    SettingsBySection::const_iterator seci = mSettings.find(section);
    if (seci == mSettings.end())
    {
        U2_EXCEPT(U2Exception::ERR_ITEM_NOT_FOUND, 
            "Cannot find section " + section, 
            "U2ConfigFile::getSettingsIterator");
    }
    else
    {
        return SettingsIterator(seci->second->begin(), seci->second->end());
    }
}
//-----------------------------------------------------------------------
U2ConfigFile::SectionIterator U2ConfigFile::getSectionIterator(void)
{
    return SectionIterator(mSettings.begin(), mSettings.end());
}